#include "U2Image.h"

#include "U2Exception.h"
#include "U2LogManager.h"
#include "U2ResourceGroupManager.h"
#include "U2ColourValue.h"
#include "U2Codec.h"
#include "U2CodecImage.h"
#include "U2DataStreamMemory.h"
#include "U2ImageResampler.h"



U2EG_NAMESPACE_USING



//-----------------------------------------------------------------------------
U2Image::U2Image()
: m_uWidth(0),
m_uHeight(0),
m_uDepth(0),
m_uSize(0),
m_uNumMipmaps(0),
m_uFlags(0),
m_eFormat(PF_UNKNOWN),
m_pBuffer( NULL ),
m_bAutoDelete( true )
{
}

//-----------------------------------------------------------------------------
U2Image::U2Image( const U2Image &img )
: m_pBuffer( NULL ),
m_bAutoDelete( true )
{
    // call assignment operator
    *this = img;
}

//-----------------------------------------------------------------------------
U2Image::~U2Image()
{
    //Only delete if this was not a dynamic image (meaning app holds & destroys buffer)
    if( m_pBuffer && m_bAutoDelete )
    {
        U2_FREE(m_pBuffer, MEMCATEGORY_GENERAL);
        m_pBuffer = NULL;
    }
}

//-----------------------------------------------------------------------------
U2Image & U2Image::operator = ( const U2Image &img )
{
    if( m_pBuffer && m_bAutoDelete )
    {
        U2_FREE(m_pBuffer, MEMCATEGORY_GENERAL);
        m_pBuffer = NULL;
    }
    m_uWidth = img.m_uWidth;
    m_uHeight = img.m_uHeight;
    m_uDepth = img.m_uDepth;
    m_eFormat = img.m_eFormat;
    m_uSize = img.m_uSize;
    m_uFlags = img.m_uFlags;
    m_ucPixelSize = img.m_ucPixelSize;
    m_uNumMipmaps = img.m_uNumMipmaps;
    m_bAutoDelete = img.m_bAutoDelete;
    //Only create/copy when previous data was not dynamic data
    if( m_bAutoDelete )
    {
        m_pBuffer = U2_ALLOC_T(u2uchar, m_uSize, MEMCATEGORY_GENERAL);
        memcpy( m_pBuffer, img.m_pBuffer, m_uSize );
    }
    else
    {
        m_pBuffer = img.m_pBuffer;
    }

    return *this;
}

//-----------------------------------------------------------------------------
U2Image & U2Image::flipAroundY()
{
    if( !m_pBuffer )
    {
        U2_EXCEPT( 
            U2Exception::ERR_INTERNAL_ERROR,
            "Can not flip an unitialized texture",
            "U2Image::flipAroundY" );
    }

    m_uNumMipmaps = 0; // U2Image operations lose precomputed mipmaps

    u2uchar	*pTempBuffer1 = NULL;
    u2ushort	*pTempBuffer2 = NULL;
    u2uchar	*pTempBuffer3 = NULL;
    u2uint	*pTempBuffer4 = NULL;

    u2uchar	*src1 = m_pBuffer, *dst1 = NULL;
    u2ushort	*src2 = (u2ushort *)m_pBuffer, *dst2 = NULL;
    u2uchar	*src3 = m_pBuffer, *dst3 = NULL;
    u2uint	*src4 = (u2uint *)m_pBuffer, *dst4 = NULL;

    u2ushort y;
    switch (m_ucPixelSize)
    {
    case 1:
        pTempBuffer1 = U2_ALLOC_T(u2uchar, m_uWidth * m_uHeight, MEMCATEGORY_GENERAL);
        for (y = 0; y < m_uHeight; y++)
        {
            dst1 = (pTempBuffer1 + ((y * m_uWidth) + m_uWidth - 1));
            for (u2ushort x = 0; x < m_uWidth; x++)
                memcpy(dst1--, src1++, sizeof(u2uchar));
        }

        memcpy(m_pBuffer, pTempBuffer1, m_uWidth * m_uHeight * sizeof(u2uchar));
        U2_FREE(pTempBuffer1, MEMCATEGORY_GENERAL);
        break;

    case 2:
        pTempBuffer2 = U2_ALLOC_T(u2ushort, m_uWidth * m_uHeight, MEMCATEGORY_GENERAL);
        for (y = 0; y < m_uHeight; y++)
        {
            dst2 = (pTempBuffer2 + ((y * m_uWidth) + m_uWidth - 1));
            for (u2ushort x = 0; x < m_uWidth; x++)
                memcpy(dst2--, src2++, sizeof(u2ushort));
        }

        memcpy(m_pBuffer, pTempBuffer2, m_uWidth * m_uHeight * sizeof(u2ushort));
        U2_FREE(pTempBuffer2, MEMCATEGORY_GENERAL);
        break;

    case 3:
        pTempBuffer3 = U2_ALLOC_T(u2uchar, m_uWidth * m_uHeight * 3, MEMCATEGORY_GENERAL);
        for (y = 0; y < m_uHeight; y++)
        {
            size_t offset = ((y * m_uWidth) + (m_uWidth - 1)) * 3;
            dst3 = pTempBuffer3;
            dst3 += offset;
            for (size_t x = 0; x < m_uWidth; x++)
            {
                memcpy(dst3, src3, sizeof(u2uchar) * 3);
                dst3 -= 3; src3 += 3;
            }
        }

        memcpy(m_pBuffer, pTempBuffer3, m_uWidth * m_uHeight * sizeof(u2uchar) * 3);
        U2_FREE(pTempBuffer3, MEMCATEGORY_GENERAL);
        break;

    case 4:
        pTempBuffer4 = U2_ALLOC_T(u2uint, m_uWidth * m_uHeight, MEMCATEGORY_GENERAL);
        for (y = 0; y < m_uHeight; y++)
        {
            dst4 = (pTempBuffer4 + ((y * m_uWidth) + m_uWidth - 1));
            for (u2ushort x = 0; x < m_uWidth; x++)
                memcpy(dst4--, src4++, sizeof(u2uint));
        }

        memcpy(m_pBuffer, pTempBuffer4, m_uWidth * m_uHeight * sizeof(u2uint));
        U2_FREE(pTempBuffer4, MEMCATEGORY_GENERAL);
        break;

    default:
        U2_EXCEPT( 
            U2Exception::ERR_INTERNAL_ERROR,
            "Unknown pixel depth",
            "U2Image::flipAroundY" );
        break;
    }

    return *this;

}

//-----------------------------------------------------------------------------
U2Image & U2Image::flipAroundX()
{
    if( !m_pBuffer )
    {
        U2_EXCEPT( 
            U2Exception::ERR_INTERNAL_ERROR,
            "Can not flip an unitialized texture",
            "U2Image::flipAroundX" );
    }

    m_uNumMipmaps = 0; // U2Image operations lose precomputed mipmaps

    size_t rowSpan = m_uWidth * m_ucPixelSize;

    u2uchar *pTempBuffer = U2_ALLOC_T(u2uchar, rowSpan * m_uHeight, MEMCATEGORY_GENERAL);
    u2uchar *ptr1 = m_pBuffer, *ptr2 = pTempBuffer + ( ( m_uHeight - 1 ) * rowSpan );

    for( u2ushort i = 0; i < m_uHeight; i++ )
    {
        memcpy( ptr2, ptr1, rowSpan );
        ptr1 += rowSpan; ptr2 -= rowSpan;
    }

    memcpy( m_pBuffer, pTempBuffer, rowSpan * m_uHeight);

    U2_FREE(pTempBuffer, MEMCATEGORY_GENERAL);

    return *this;
}

//-----------------------------------------------------------------------------
U2Image& U2Image::loadDynamicImage( u2uchar* pData, size_t uWidth, size_t uHeight, 
                               size_t depth,
                               PixelFormat eFormat, bool autoDelete, 
                               size_t numFaces, size_t numMipMaps)
{

    if( m_pBuffer && m_bAutoDelete )
    {
        U2_FREE(m_pBuffer, MEMCATEGORY_GENERAL);
        m_pBuffer = NULL;
    }
    // Set image metadata
    m_uWidth = uWidth;
    m_uHeight = uHeight;
    m_uDepth = depth;
    m_eFormat = eFormat;
    m_ucPixelSize = static_cast<u2uchar>(U2PixelUtil::getNumElemBytes( m_eFormat ));
    m_uNumMipmaps = numMipMaps;
    m_uFlags = 0;
    // Set flags
    if (U2PixelUtil::isCompressed(eFormat))
        m_uFlags |= IF_COMPRESSED;
    if (m_uDepth != 1)
        m_uFlags |= IF_3D_TEXTURE;
    if(numFaces == 6)
        m_uFlags |= IF_CUBEMAP;
    if(numFaces != 6 && numFaces != 1)
        U2_EXCEPT(U2Exception::ERR_INVALIDPARAMS, 
        "Number of faces currently must be 6 or 1.", 
        "U2Image::loadDynamicImage");

    m_uSize = calculateSize(numMipMaps, numFaces, uWidth, uHeight, depth, eFormat);
    m_pBuffer = pData;
    m_bAutoDelete = autoDelete;

    return *this;

}

//-----------------------------------------------------------------------------
U2Image & U2Image::loadRawData(
                           U2DataStreamPtr& stream, 
                           size_t uWidth, size_t uHeight, size_t uDepth,
                           PixelFormat eFormat,
                           size_t numFaces, size_t numMipMaps)
{

    size_t size = calculateSize(numMipMaps, numFaces, uWidth, uHeight, uDepth, eFormat);
    if (size != stream->size())
    {
        U2_EXCEPT(U2Exception::ERR_INVALIDPARAMS, 
            "Stream size does not match calculated image size", 
            "U2Image::loadRawData");
    }

    u2uchar *buffer = U2_ALLOC_T(u2uchar, size, MEMCATEGORY_GENERAL);
    stream->read(buffer, size);

    return loadDynamicImage(buffer,
        uWidth, uHeight, uDepth,
        eFormat, true, numFaces, numMipMaps);

}
//-----------------------------------------------------------------------------
U2Image & U2Image::load(const U2String& strFileName, const U2String& group)
{

    U2String strExt;

    size_t pos = strFileName.find_last_of(".");
    if( pos != U2String::npos && pos < (strFileName.length() - 1))
    {
        strExt = strFileName.substr(pos+1);
    }

    U2DataStreamPtr encoded = U2ResourceGroupManager::getSingleton().openResource(strFileName, group);
    return load(encoded, strExt);

}
//-----------------------------------------------------------------------------
void U2Image::save(const U2String& filename)
{
    if( !m_pBuffer )
    {
        U2_EXCEPT(U2Exception::ERR_INVALIDPARAMS, "No image data loaded", 
            "U2Image::save");
    }

    U2String strExt;
    size_t pos = filename.find_last_of(".");
    if( pos == U2String::npos )
        U2_EXCEPT(
        U2Exception::ERR_INVALIDPARAMS, 
        "Unable to save image file '" + filename + "' - invalid extension.",
        "U2Image::save" );

    while( pos != filename.length() - 1 )
        strExt += filename[++pos];

    U2Codec * pCodec = U2Codec::getCodec(strExt);
    if( !pCodec )
        U2_EXCEPT(
        U2Exception::ERR_INVALIDPARAMS, 
        "Unable to save image file '" + filename + "' - invalid extension.",
        "U2Image::save" );

    U2CodecImage::ImageData* imgData = U2_NEW U2CodecImage::ImageData();
    imgData->format = m_eFormat;
    imgData->height = m_uHeight;
    imgData->width = m_uWidth;
    imgData->depth = m_uDepth;
    // Wrap in CodecDataPtr, this will delete
    U2Codec::CodecDataPtr codeDataPtr(imgData);
    // Wrap memory, be sure not to delete when stream destroyed
    U2DataStreamMemoryPtr wrapper(U2_NEW U2DataStreamMemory(m_pBuffer, m_uSize, false));

    pCodec->codeToFile(wrapper, filename, codeDataPtr);
}
//---------------------------------------------------------------------
U2DataStreamPtr U2Image::encode(const U2String& formatextension)
{
    if( !m_pBuffer )
    {
        U2_EXCEPT(U2Exception::ERR_INVALIDPARAMS, "No image data loaded", 
            "U2Image::encode");
    }

    U2Codec * pCodec = U2Codec::getCodec(formatextension);
    if( !pCodec )
        U2_EXCEPT(
        U2Exception::ERR_INVALIDPARAMS, 
        "Unable to encode image data as '" + formatextension + "' - invalid extension.",
        "U2Image::encode" );

    U2CodecImage::ImageData* imgData = U2_NEW U2CodecImage::ImageData();
    imgData->format = m_eFormat;
    imgData->height = m_uHeight;
    imgData->width = m_uWidth;
    imgData->depth = m_uDepth;
    // Wrap in CodecDataPtr, this will delete
    U2Codec::CodecDataPtr codeDataPtr(imgData);
    // Wrap memory, be sure not to delete when stream destroyed
    U2DataStreamMemoryPtr wrapper(U2_NEW U2DataStreamMemory(m_pBuffer, m_uSize, false));

    return pCodec->code(wrapper, codeDataPtr);
}
//-----------------------------------------------------------------------------
U2Image & U2Image::load(U2DataStreamPtr& stream, const U2String& type )
{
    if( m_pBuffer && m_bAutoDelete )
    {
        U2_FREE(m_pBuffer, MEMCATEGORY_GENERAL);
        m_pBuffer = NULL;
    }

    U2Codec * pCodec = 0;
    if (!type.empty())
    {
        // use named codec
        pCodec = U2Codec::getCodec(type);
    }
    else
    {
        // derive from magic number
        // read the first 32 bytes or file size, if less
        size_t magicLen = std::min(stream->size(), (size_t)32);
        char magicBuf[32];
        stream->read(magicBuf, magicLen);
        // return to start
        stream->seek(0);
        pCodec = U2Codec::getCodec(magicBuf, magicLen);
    }

    if( !pCodec )
    {
        U2_EXCEPT(
        U2Exception::ERR_INVALIDPARAMS, 
        "Unable to load image - unable to identify codec. Check file extension "
        "and file format.",
        "U2Image::load" );
    }

    U2Codec::DecodeResult res = pCodec->decode(stream);

    U2CodecImage::ImageData* pData = 
        static_cast<U2CodecImage::ImageData*>(res.second.getPointer());

    m_uWidth = pData->width;
    m_uHeight = pData->height;
    m_uDepth = pData->depth;
    m_uSize = pData->size;
    m_uNumMipmaps = pData->num_mipmaps;
    m_uFlags = pData->flags;

    // Get the format and compute the pixel size
    m_eFormat = pData->format;
    m_ucPixelSize = static_cast<u2uchar>(U2PixelUtil::getNumElemBytes( m_eFormat ));
    // Just use internal buffer of returned memory stream
    m_pBuffer = res.first->getPtr();
    // Make sure stream does not delete
    res.first->setFreeOnClose(false);

    return *this;
}
//---------------------------------------------------------------------
U2String U2Image::getFileExtFromMagic(U2DataStreamPtr stream)
{
    // read the first 32 bytes or file size, if less
    size_t magicLen = std::min(stream->size(), (size_t)32);
    char magicBuf[32];
    stream->read(magicBuf, magicLen);
    // return to start
    stream->seek(0);
    U2Codec* pCodec = U2Codec::getCodec(magicBuf, magicLen);

    if(pCodec)
        return pCodec->getType();
    else
        return U2StringUtil::BLANK;

}
//-----------------------------------------------------------------------------
u2uchar* U2Image::getData()
{
    return m_pBuffer;
}

//-----------------------------------------------------------------------------
const u2uchar* U2Image::getData() const
{
    assert( m_pBuffer );
    return m_pBuffer;
}

//-----------------------------------------------------------------------------
size_t U2Image::getSize() const
{
    return m_uSize;
}

//-----------------------------------------------------------------------------
size_t U2Image::getNumMipmaps() const
{
    return m_uNumMipmaps;
}

//-----------------------------------------------------------------------------
bool U2Image::hasFlag(const ImageFlags imgFlag) const
{
    if(m_uFlags & imgFlag)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//-----------------------------------------------------------------------------
size_t U2Image::getDepth() const
{
    return m_uDepth;
}
//-----------------------------------------------------------------------------
size_t U2Image::getWidth() const
{
    return m_uWidth;
}

//-----------------------------------------------------------------------------
size_t U2Image::getHeight() const
{
    return m_uHeight;
}
//-----------------------------------------------------------------------------
size_t U2Image::getNumFaces(void) const
{
    if(hasFlag(IF_CUBEMAP))
        return 6;
    return 1;
}
//-----------------------------------------------------------------------------
size_t U2Image::getRowSpan() const
{
    return m_uWidth * m_ucPixelSize;
}

//-----------------------------------------------------------------------------
PixelFormat U2Image::getFormat() const
{
    return m_eFormat;
}

//-----------------------------------------------------------------------------
u2uchar U2Image::getBPP() const
{
    return m_ucPixelSize * 8;
}

//-----------------------------------------------------------------------------
bool U2Image::getHasAlpha(void) const
{
    return U2PixelUtil::getFlags(m_eFormat) & PFF_HASALPHA;
}
//-----------------------------------------------------------------------------
void U2Image::applyGamma( unsigned char *buffer, u2real gamma, size_t size, u2uchar bpp )
{
    if( gamma == 1.0f )
        return;

    //NB only 24/32-bit supported
    if( bpp != 24 && bpp != 32 ) return;

    u2uint stride = bpp >> 3;

    for( size_t i = 0, j = size / stride; i < j; i++, buffer += stride )
    {
        float r, g, b;

        r = (float)buffer[0];
        g = (float)buffer[1];
        b = (float)buffer[2];

        r = r * gamma;
        g = g * gamma;
        b = b * gamma;

        float scale = 1.0f, tmp;

        if( r > 255.0f && (tmp=(255.0f/r)) < scale )
            scale = tmp;
        if( g > 255.0f && (tmp=(255.0f/g)) < scale )
            scale = tmp;
        if( b > 255.0f && (tmp=(255.0f/b)) < scale )
            scale = tmp;

        r *= scale; g *= scale; b *= scale;

        buffer[0] = (u2uchar)r;
        buffer[1] = (u2uchar)g;
        buffer[2] = (u2uchar)b;
    }
}
//-----------------------------------------------------------------------------
void U2Image::resize(u2ushort width, u2ushort height, Filter filter)
{
    // resizing dynamic images is not supported
    assert(m_bAutoDelete);
    assert(m_uDepth == 1);

    // reassign buffer to temp image, make sure auto-delete is true
    U2Image temp;
    temp.loadDynamicImage(m_pBuffer, m_uWidth, m_uHeight, 1, m_eFormat, true);
    // do not delete[] m_pBuffer!  temp will destroy it

    // set new dimensions, allocate new buffer
    m_uWidth = width;
    m_uHeight = height;
    m_uSize = U2PixelUtil::getMemorySize(m_uWidth, m_uHeight, 1, m_eFormat);
    m_pBuffer = U2_ALLOC_T(u2uchar, m_uSize, MEMCATEGORY_GENERAL);
    m_uNumMipmaps = 0; // Loses precomputed mipmaps

    // scale the image from temp into our resized buffer
    U2Image::scale(temp.getPixelBox(), getPixelBox(), filter);
}
//-----------------------------------------------------------------------
void U2Image::scale(const U2PixelBox &src, const U2PixelBox &scaled, Filter filter) 
{
    assert(U2PixelUtil::isAccessible(src.format));
    assert(U2PixelUtil::isAccessible(scaled.format));
    U2DataStreamMemoryPtr buf; // For auto-delete
    U2PixelBox temp;
    switch (filter) 
    {
    default:
    case FILTER_NEAREST:
        if(src.format == scaled.format) 
        {
            // No intermediate buffer needed
            temp = scaled;
        }
        else
        {
            // Allocate temporary buffer of destination size in source format 
            temp = U2PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format);
            buf.bind(U2_NEW U2DataStreamMemory(temp.getConsecutiveSize()));
            temp.data = buf->getPtr();
        }
        // super-optimized: no conversion
        switch (U2PixelUtil::getNumElemBytes(src.format)) 
        {
        case 1: NearestResampler<1>::scale(src, temp); break;
        case 2: NearestResampler<2>::scale(src, temp); break;
        case 3: NearestResampler<3>::scale(src, temp); break;
        case 4: NearestResampler<4>::scale(src, temp); break;
        case 6: NearestResampler<6>::scale(src, temp); break;
        case 8: NearestResampler<8>::scale(src, temp); break;
        case 12: NearestResampler<12>::scale(src, temp); break;
        case 16: NearestResampler<16>::scale(src, temp); break;
        default:
            // never reached
            assert(false);
        }
        if(temp.data != scaled.data)
        {
            // Blit temp buffer
            U2PixelUtil::bulkPixelConversion(temp, scaled);
        }
        break;

    case FILTER_LINEAR:
    case FILTER_BILINEAR:
        switch (src.format) 
        {
        case PF_L8: case PF_A8: case PF_BYTE_LA:
        case PF_R8G8B8: case PF_B8G8R8:
        case PF_R8G8B8A8: case PF_B8G8R8A8:
        case PF_A8B8G8R8: case PF_A8R8G8B8:
        case PF_X8B8G8R8: case PF_X8R8G8B8:
            if(src.format == scaled.format) 
            {
                // No intermediate buffer needed
                temp = scaled;
            }
            else
            {
                // Allocate temp buffer of destination size in source format 
                temp = U2PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format);
                buf.bind(U2_NEW U2DataStreamMemory(temp.getConsecutiveSize()));
                temp.data = buf->getPtr();
            }
            // super-optimized: byte-oriented math, no conversion
            switch (U2PixelUtil::getNumElemBytes(src.format)) 
            {
            case 1: LinearResampler_Byte<1>::scale(src, temp); break;
            case 2: LinearResampler_Byte<2>::scale(src, temp); break;
            case 3: LinearResampler_Byte<3>::scale(src, temp); break;
            case 4: LinearResampler_Byte<4>::scale(src, temp); break;
            default:
                // never reached
                assert(false);
            }
            if(temp.data != scaled.data)
            {
                // Blit temp buffer
                U2PixelUtil::bulkPixelConversion(temp, scaled);
            }
            break;
        case PF_FLOAT32_RGB:
        case PF_FLOAT32_RGBA:
            if (scaled.format == PF_FLOAT32_RGB || scaled.format == PF_FLOAT32_RGBA)
            {
                // float32 to float32, avoid unpack/repack overhead
                LinearResampler_Float32::scale(src, scaled);
                break;
            }
            // else, fall through
        default:
            // non-optimized: floating-point math, performs conversion but always works
            LinearResampler::scale(src, scaled);
        }
        break;
    }
}

//-----------------------------------------------------------------------------    

U2ColourValue U2Image::getColourAt(int x, int y, int z) const
{
    U2ColourValue rval;
    U2PixelUtil::unpackColour(&rval, m_eFormat, &m_pBuffer[m_ucPixelSize * (z * m_uWidth * m_uHeight + m_uWidth * y + x)]);
    return rval;
}

//-----------------------------------------------------------------------------    

U2PixelBox U2Image::getPixelBox(size_t face, size_t mipmap) const
{
    // U2Image data is arranged as:
    // face 0, top level (mip 0)
    // face 0, mip 1
    // face 0, mip 2
    // face 1, top level (mip 0)
    // face 1, mip 1
    // face 1, mip 2
    // etc
    if(mipmap > getNumMipmaps())
        U2_EXCEPT( U2Exception::ERR_NOT_IMPLEMENTED,
        "Mipmap index out of range",
        "U2Image::getPixelBox" ) ;
    if(face >= getNumFaces())
        U2_EXCEPT(U2Exception::ERR_INVALIDPARAMS, "Face index out of range",
        "U2Image::getPixelBox");
    // Calculate mipmap offset and size
    u2uint8 *offset = const_cast<u2uint8*>(getData());
    // Base offset is number of full faces
    size_t width = getWidth(), height=getHeight(), depth=getDepth();
    size_t numMips = getNumMipmaps();

    // Figure out the offsets 
    size_t fullFaceSize = 0;
    size_t finalFaceSize = 0;
    size_t finalWidth, finalHeight, finalDepth;
    for(size_t mip=0; mip <= numMips; ++mip)
    {
        if (mip == mipmap)
        {
            finalFaceSize = fullFaceSize;
            finalWidth = width;
            finalHeight = height;
            finalDepth = depth;
        }
        fullFaceSize += U2PixelUtil::getMemorySize(width, height, depth, getFormat());

        /// Half size in each dimension
        if(width!=1) width /= 2;
        if(height!=1) height /= 2;
        if(depth!=1) depth /= 2;
    }
    // Advance pointer by number of full faces, plus mip offset into
    offset += face * fullFaceSize;
    offset += finalFaceSize;
    // Return subface as pixelbox
    U2PixelBox src(finalWidth, finalHeight, finalDepth, getFormat(), offset);
    return src;
}
//-----------------------------------------------------------------------------    
size_t U2Image::calculateSize(size_t mipmaps, size_t faces, size_t width, size_t height, size_t depth, 
                            PixelFormat format)
{
    size_t size = 0;
    for(size_t mip=0; mip<=mipmaps; ++mip)
    {
        size += U2PixelUtil::getMemorySize(width, height, depth, format)*faces; 
        if(width!=1) width /= 2;
        if(height!=1) height /= 2;
        if(depth!=1) depth /= 2;
    }
    return size;
}